
package es.ceu.mp2.conway;

import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.apache.log4j.Logger;


public class XMLReader implements Reader{

    public static final String TAG_ALIVECELL = "CelulaViva";
    public static final String TAG_X = "X";
    public static final String TAG_Y = "Y";
    private static final Logger log = Logger.getLogger(XMLReader.class);

    public GameBoard read(String fileName) {
        try{
            log.debug("Se va a abrir el fichero");
            final Document document = getDocument(fileName);
            log.debug("Fichero leido correctamente");
            return readCells(document);
        }catch (InvalidInputStreamException ex){
            log.error("Error. El fichero no existe: "+ex.getMessage());
            throw new FileFormatException("Error. El fichero no existe");
        }
    }

    private Document getDocument(String fileName) {
         try {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            return db.parse(new File(fileName));
        } catch (SAXException ex) {
            log.error(" Fichero xml creado incorrectamente. "+ex.getMessage());
            throw new FileFormatException(" Fichero xml creado incorrectamente. ", ex);
        } catch (IOException ex) {
            log.error(" El input esta provocando errores de IO: "+ex.getMessage());
            throw new InvalidInputStreamException(" el input esta provocando errores de IO", ex);
        } catch (ParserConfigurationException ex) {
            log.error(" Error en la configuracion del parser de xml: "+ex.getMessage());
            throw new XmlConfigurationReaderException(" Error en la configuracion del parser de xml", ex);
        }
     }

    private GameBoard readCells(final Document document) {
        GameBoard board;  
        Node size = document.getFirstChild();
        NamedNodeMap n = size.getAttributes();

        if (n.getLength() != 2){
            log.error(" Fichero xml creado incorrectamente. No tiene los dos parámetros de tamaño requeridos");
            throw new FileFormatException(" Fichero xml creado incorrectamente. No tiene los dos parámetros de tamaño requeridos");
        }

        int rows = Integer.parseInt(n.item(0).getNodeValue());
        int columns = Integer.parseInt(n.item(1).getNodeValue());

        board = new GameBoard(rows,columns);
        
        NodeList cells = document.getElementsByTagName(TAG_ALIVECELL);
        for (int i = 0; i < cells.getLength(); i++)
           readCell(board, cells.item(i));

        return board;
    }

    private void readCell(GameBoard board, Node cellNode) throws DOMException {
        int row = -1, column = -1;
        NodeList nodeChilds = cellNode.getChildNodes();

        for (int i = 0; i < nodeChilds.getLength(); i++) {
            Node nodo = nodeChilds.item(i);
            if (isAnElementNode(nodo)) {
                final String nodeName = nodo.getNodeName();
                if (TAG_X.equals(nodeName))
                    row = Integer.parseInt(getNodeText(nodo));
                else if (TAG_Y.equals(nodeName))
                    column = Integer.parseInt(getNodeText(nodo));
                else{
                    log.error(" Fichero xml creado incorrectamente. Campo no reconocido.");
                    throw new FileFormatException(" Fichero xml creado incorrectamente. Campo no reconocido.");
                }
            }
        }

        if (row != -1 && column != -1)
            board.setCellAlive(row, column);
        else{
            log.error(" Fichero xml creado incorrectamente. Campo no reconocido. No se han encontrado los dos campos obligatorios");
            throw new FileFormatException( " Fichero xml creado incorrectamente. No se han encontrado los dos campos obligatorios");
        }

    }

    private boolean isAnElementNode(Node nodo) {
        return nodo.getNodeType() == Node.ELEMENT_NODE;
    }

    private String getNodeText(Node nodo) {
        if (nodo.getChildNodes().getLength() == 0) {
            return "";
        }
        return nodo.getChildNodes().item(0).getNodeValue();
    }

}
